package demo.netty.nio;

import org.springframework.util.StringUtils;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;

public class TimeServer {


    public static void main(String[] args) throws InterruptedException {
        int port = 8080;
        if (args != null && args.length > 0) {
            try {
                port = Integer.parseInt(args[0]);
            } catch (NumberFormatException e) {
                //
            }
        }
        new Thread(new MultiplexerTimeServer(port)).start();
        Thread.currentThread().join();
    }


    public static class MultiplexerTimeServer implements Runnable {

        private Selector selector;

        private ServerSocketChannel serverSocketChannel;

        private volatile boolean stop;


        public MultiplexerTimeServer(int port) {
            try {
                selector = Selector.open();
                serverSocketChannel = ServerSocketChannel.open();
                serverSocketChannel.configureBlocking(false);
                serverSocketChannel.socket().bind(new InetSocketAddress(port), 1024);
                //注册一个接收的监听事件
                serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
                System.out.println("The time server is start int port :" + port);
            } catch (IOException e) {
                System.exit(1);
            }
        }

        public void stop() {
            this.stop = true;
        }


        @Override
        public void run() {
            try {
                while (!stop) {
                    //轮询selector上面的事件
                    selector.select(1000);
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
                    SelectionKey key = null;
                    while (keyIterator.hasNext()) {
                        key = keyIterator.next();
                        keyIterator.remove();
                        this.handleInput(key);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        /**
         * 处理事件
         * @param selectionKey
         * @throws IOException
         */
        private void handleInput(SelectionKey selectionKey) throws IOException {
            if (selectionKey.isValid()) {
                //如果是接收事件，处理后再注册一个读事件
                if (selectionKey.isAcceptable()) {
                    System.out.println("接收到连接事件。。。。");
                    ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();
                    SocketChannel sc = channel.accept();
                    sc.configureBlocking(false);
                    sc.register(selector, SelectionKey.OP_READ);
                }

                //如果是读事件
                if (selectionKey.isReadable()) {
                    System.out.println("接收到读事件。。。。");
                    SocketChannel channel = (SocketChannel) selectionKey.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    int read = channel.read(byteBuffer);
                    byteBuffer.flip();
                    if (read > 0) {
                        byte[] bytes = new byte[byteBuffer.remaining()];
                        byteBuffer.get(bytes);
                        String body = new String(bytes, StandardCharsets.UTF_8);
                        System.out.println("The time server receive order :" + body);
                        //返回客户端时间
                        String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
                        doWrite(channel, currentTime);
                    } else if (read < 0) {
                        selectionKey.cancel();
                        channel.close();
                    } else {
                        ; //读取 0 字节 忽略
                    }
                }
            }

        }

        private void doWrite(SocketChannel socketChannel, String response) throws IOException {
            if (StringUtils.hasText(response)) {
                byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
                ByteBuffer wrap = ByteBuffer.allocate(bytes.length);
                wrap.put(bytes);
                wrap.flip();
                socketChannel.write(wrap);
            }
        }
    }

}
